package net.love5z.web.service;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;

import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;

import com.xmxsuperstar.lib.codec.Base64;

@Service
public class PacGeneratorService {

    private String getGFWList() {
        BufferedReader gfwlistR = null;
        try {
            URL gfwlistUrl = new URL("http://autoproxy-gfwlist.googlecode.com"
                                     + "/svn/trunk/gfwlist.txt");
            URLConnection gfwlistConn = gfwlistUrl.openConnection();
            gfwlistR = new BufferedReader(new InputStreamReader(gfwlistConn.getInputStream()));
        } catch (IOException e1) {
            //TODO:
        }

        StringBuilder gfwlistContent = new StringBuilder();

        try {
            String line = null;
            while ((line = gfwlistR.readLine()) != null) {
                gfwlistContent.append(line).append("\n");
            }
        } catch (IOException e) {
            //TODO:
        }

        return gfwlistContent.toString();
    }

    private String getRule() {
        String ruleString = null;
        try {
            ruleString = new String(Base64.decode(this.getGFWList().replace("\n", "")), "UTF-8");
        } catch (UnsupportedEncodingException e) {
        }
        return ruleString;
    }

    public String getPac(String userRuleText, String hostName, int port) {
        StringBuilder pacContent = new StringBuilder();

        try {

            StringBuffer allRulesBuffer = new StringBuffer();

            if (StringUtils.isNotBlank(userRuleText)) {
                allRulesBuffer.append(userRuleText);
            }
            allRulesBuffer.append(this.getRule());

            BufferedReader ruleR = new BufferedReader(new InputStreamReader(
                new ByteArrayInputStream(allRulesBuffer.toString().getBytes("ISO-8859-1"))));

            pacContent.append("function FindProxyForURL(url, host) {").append("\n");
            pacContent.append("\t").append("var PROXY = \"SOCKS5 " + hostName + ":" + port + "\";")
                .append("\n");
            pacContent.append("\t").append("var DEFAULT = \"DIRECT\";").append("\n");

            String line = null;

            File customRuleFile = new File("etc/gfwlist.txt");
            if (customRuleFile.exists()) {
                BufferedReader customeRuleR = new BufferedReader(new InputStreamReader(
                    new FileInputStream(customRuleFile)));
                while ((line = customeRuleR.readLine()) != null) {
                    pacContent.append(convertRule(line));
                }
            }

            while ((line = ruleR.readLine()) != null) {
                pacContent.append(convertRule(line));
            }
            pacContent.append("\t").append("return DEFAULT;").append("\n");
            pacContent.append("}\n");

        } catch (IOException e) {
            //TODO:
        }

        return pacContent.toString();
    }

    private String convertRule(String ruleLine) {
        if (StringUtils.isBlank(ruleLine)) {
            return "";
        }

        if (ruleLine.startsWith("[") || ruleLine.startsWith("!")) {
            return "";
        }

        String returnProxy = "PROXY";
        String ruleReg = "";

        if (ruleLine.startsWith("@@")) {
            ruleLine = ruleLine.substring(2);
            returnProxy = "DEFAULT";
        }

        if (ruleLine.startsWith("||")) {
            ruleReg = "/^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?" + regEncode(ruleLine.substring(2))
                      + "/";
        } else if (ruleLine.startsWith("|")) {
            ruleReg = "/^" + regEncode(ruleLine.substring(1)) + "/";
        } else if (ruleLine.startsWith("/") && ruleLine.endsWith("/")) {
            ruleReg = ruleLine;
        } else {
            ruleReg = "/" + regEncode(ruleLine) + "/";
        }

        StringBuilder pacLine = new StringBuilder();
        pacLine.append("\t").append("if(").append(ruleReg).append("i.test(url)) return ")
            .append(returnProxy).append(";\n");
        return pacLine.toString();

    }

    private String regEncode(String str) {
        return str.replace("/", "\\/").replace(".", "\\.").replace(":", "\\:").replace("%", "\\%")
            .replace("*", ".*").replace("-", "\\-").replace("&", "\\&").replace("?", "\\?");
    }
}
